/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.commons.utils;

import io.iec.edp.caf.multicontext.context.PlatformApplicationContext;
import io.iec.edp.caf.multicontext.support.ModuleManager;
import org.springframework.context.ApplicationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 反射工具
 */
public class InvokeService {
    /**
     * 根据类名获取类信息
     *
     * @param className 类名
     * @return 类信息
     */
    public static Class<?> getClass(String className) {
        try {
            return getClassByName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("类不存在 " + className);
        }
    }

    /**
     * 反射从类加载器里取class
     */
    public static Class getClassByName(String name) throws ClassNotFoundException {
        try {
            return Class.forName(name, true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            ApplicationContext ctx = SpringBeanUtils.getApplicationContext();
            if (ctx instanceof PlatformApplicationContext) {
                ModuleManager moduleManager = ((PlatformApplicationContext) ctx).getModuleManager();
                return moduleManager.getClassByName(name);
            }
        }

        throw new ClassNotFoundException(name);
    }

    /**
     * 根据类型及参数创建对象
     *
     * @param clazz  类型
     * @param params 参数
     * @return 实例化对象
     */
    public static Object createInstance(Class clazz, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        Object value = null;
        try {
            if (clazz != null) {
                Constructor construtor = clazz.getConstructor(paramsType);
                value = construtor.newInstance(params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("构造函数不存在 " + clazz.getName());
        } catch (InstantiationException e) {
            throw new RuntimeException("实例化失败 " + clazz.getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + clazz.getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + clazz.getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + clazz.getName());
        }
        return value;
    }

    /**
     * 根据类名及参数创建对象
     *
     * @param className 类名
     * @param params    参数
     * @return 实例化对象
     */
    public static Object createInstance(String className, Object... params) {
        Class<?> clazz = getClass(className);
        return createInstance(clazz, params);
    }

    /**
     * 设置相应属性的值
     *
     * @param entity        实体
     * @param propertyName  属性名
     * @param propertyValue 属性值
     */
    public static void setPropertyValue(Object entity, String propertyName, Object propertyValue) {
        try {
            if (entity != null && propertyName != null) {
                Field field = entity.getClass().getField(propertyName);
                field.set(entity, propertyValue);
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("属性不存在 " + entity.getClass().getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + entity.getClass().getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + entity.getClass().getName());
        }
    }

    /**
     * 获取静态属性值
     *
     * @param className    类名
     * @param propertyName 属性名
     * @return 静态属性值
     */
    public static Object getStaticPropertyValue(String className, String propertyName) {
        Class<?> clazz = getClass(className);
        Object value = null;
        try {
            if (propertyName != null) {
                Field field = clazz.getField(propertyName);
                value = field.get(null);
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("属性不存在 " + className);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + className);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + className);
        }
        return value;
    }

    /**
     * 获取相应属性的值
     *
     * @param entity       实体
     * @param propertyName 属性名
     * @return 属性值
     */
    public static Object getPropertyValue(Object entity, String propertyName) {
        Object value = null;
        try {
            if (entity != null && propertyName != null) {
                Field field = entity.getClass().getField(propertyName);
                value = field.get(entity);
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("属性不存在 " + entity.getClass().getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + entity.getClass().getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + entity.getClass().getName());
        }
        return value;
    }

    /**
     * 反射调用静态方法
     *
     * @param clazz      类型
     * @param methodName 方法名
     * @param params     方法参数
     * @return 执行方法返回的结果
     */
    public static Object invokeStaticMethod(Class clazz, String methodName, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        Object value = null;
        try {
            if (clazz != null && methodName != null) {
                Method method = clazz.getMethod(methodName, paramsType);
                method.setAccessible(true);
                value = method.invoke(null, params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + clazz.getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + clazz.getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + clazz.getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + clazz.getName());
        }
        return value;
    }

    /**
     * 反射调用泛型静态方法
     *
     * @param clazz      类型
     * @param methodName 泛型方法名
     * @param params     方法参数
     * @return 执行方法返回的结果
     */
    public static Object invokeStaticGenericMethod(Class clazz, String methodName, Object... params) {
        Class<?>[] objectClass = getObjectClass(params);
        Object value = null;
        try {
            if (clazz != null && methodName != null) {
                Method method = clazz.getMethod(methodName, objectClass);
                method.setAccessible(true);
                value = method.invoke(null, params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + clazz.getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + clazz.getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + clazz.getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + clazz.getName());
        }
        return value;
    }

    /**
     * 反射调用无返回值静态方法
     *
     * @param clazz      类型
     * @param methodName 方法名
     * @param params     方法参数
     */
    public static void invokeStaticMethodWithoutResult(Class clazz, String methodName, Object... params) {
        invokeStaticMethod(clazz, methodName, params);
    }

    /**
     * 反射调用无返回值泛型静态方法
     *
     * @param clazz      类型
     * @param methodName 泛型方法名
     * @param params     方法参数
     */
    public static void invokeStaticGenericMethodWithoutResult(Class clazz, String methodName, Object... params) {
        invokeStaticGenericMethod(clazz, methodName, params);
    }

    /**
     * 反射受保护的调用方法
     *
     * @param entity     实体
     * @param methodName 方法名
     * @param params     方法参数
     * @return 方法执行后的结果
     */
    public static Object invokeProtectedMethod(Object entity, String methodName, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        Object value = null;
        try {
            if (entity != null && methodName != null) {
                Method method = entity.getClass().getDeclaredMethod(methodName, paramsType);
                method.setAccessible(true);
                value = method.invoke(entity, params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + entity.getClass().getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + entity.getClass().getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + entity.getClass().getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + entity.getClass().getName());
        }
        return value;
    }

    /**
     * 反射调用方法
     *
     * @param entity     实体
     * @param methodName 方法名
     * @param params     方法参数
     * @return 方法执行后的结果
     */
    public static Object invokeMethod(Object entity, String methodName, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        Object value = null;
        try {
            if (entity != null && methodName != null) {
                Method method = entity.getClass().getMethod(methodName, paramsType);
                method.setAccessible(true);
                value = method.invoke(entity, params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + entity.getClass().getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + entity.getClass().getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + entity.getClass().getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + entity.getClass().getName());
        }
        return value;
    }

    /**
     * 反射调用泛型方法
     *
     * @param entity     实体
     * @param methodName 方法名
     * @param params     方法参数
     * @return 方法执行后的结果
     */
    public static Object invokeGenericMethod(Object entity, String methodName, Object... params) {
        Class<?>[] objectClass = getObjectClass(params);
        Object value = null;
        try {
            if (entity != null && methodName != null) {
                Method method = entity.getClass().getMethod(methodName, objectClass);
                method.setAccessible(true);
                value = method.invoke(entity, params);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + entity.getClass().getName());
        } catch (IllegalAccessException e) {
            throw new RuntimeException("非法访问 " + entity.getClass().getName());
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("参数错误 " + entity.getClass().getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("调用目标错误 " + entity.getClass().getName());
        }
        return value;
    }

    /**
     * 反射调用无返回值方法
     *
     * @param entity     实体
     * @param methodName 方法名
     * @param params     方法参数
     */
    public static void invokeMethodWithoutResult(Object entity, String methodName, Object... params) {
        invokeMethod(entity, methodName, params);
    }

    /**
     * 反射调用无返回值泛型方法
     *
     * @param entity     实体
     * @param methodName 方法名
     * @param params     方法参数
     */
    public static void invokeGenericMethodWithoutResult(Object entity, String methodName, Object... params) {
        invokeGenericMethod(entity, methodName, params);
    }

    /**
     * 获取类上所有注解
     *
     * @param clazz 类型
     * @return 类上注解
     */
    public static List<Annotation> getClassAnnotations(Class clazz) {
        if (clazz != null) {
            return Arrays.asList(clazz.getAnnotations());
        } else {
            return null;
        }
    }

    /**
     * 获取方法上所有注解
     *
     * @param clazz      类型
     * @param methodName 方法
     * @param params     方法参数
     * @return 方法上注解
     */
    public static List<Annotation> getMethodAnnotations(Class clazz, String methodName, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        List<Annotation> result = null;
        try {
            if (clazz != null && methodName != null) {
                Method method = clazz.getMethod(methodName, paramsType);
                method.setAccessible(true);
                result = Arrays.asList(method.getAnnotations());
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + clazz.getName());
        }
        return result;
    }

    /**
     * 获取类上指定注解
     *
     * @param clazz 类型
     * @return 类上指定注解
     */
    public static Annotation getClassAnnotation(Class clazz, Class annotationClass) {
        if (clazz != null && annotationClass != null) {
            return clazz.getAnnotation(annotationClass);
        } else {
            return null;
        }
    }

    /**
     * 获取方法上指定注解
     *
     * @param clazz           类型
     * @param annotationClass 注解类型
     * @param methodName      方法名
     * @param params          方法参数
     * @return 方法上指定注解
     */
    public static Annotation getMethodAnnotation(Class clazz, Class annotationClass, String methodName, Object... params) {
        Class<?>[] paramsType = getParamsType(params);
        Annotation annotation = null;
        try {
            if (clazz != null && annotationClass != null && methodName != null) {
                Method method = clazz.getMethod(methodName, paramsType);
                method.setAccessible(true);
                annotation = method.getAnnotation(annotationClass);
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("方法不存在 " + clazz.getName());
        }
        return annotation;
    }

    /**
     * 获取参数类型
     *
     * @param params 参数
     * @return 参数类型
     */
    private static Class<?>[] getParamsType(Object... params) {
        Class<?>[] paramsType;
        if (params == null || params.length == 0) {
            paramsType = null;
        } else {
            paramsType = new Class[params.length];
            for (int i = 0; i < params.length; i++) {
                paramsType[i] = params[i].getClass();
            }
        }
        return paramsType;
    }

    /**
     * 获取泛型方法中参数类型
     *
     * @param params 参数
     * @return 泛型方法中参数类型
     */
    private static Class<?>[] getObjectClass(Object... params) {
        Class<?>[] objectClass;
        if (params == null || params.length == 0) {
            objectClass = null;
        } else {
            objectClass = new Class[params.length];
            for (int i = 0; i < params.length; i++) {
                objectClass[i] = Object.class;
            }
        }
        return objectClass;
    }
}
